/*-
 * Public platform independent Near Field Communication (NFC) library
 *
 * Copyright (C) 2010 Roel Verdult
 * Copyright (C) 2011 Romain Tartière
 * Copyright (C) 2010, 2011, 2012 Romuald Conty
 *
 * This program is free software: you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by the
 * Free Software Foundation, either version 3 of the License, or (at your
 * option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
 */

/**
 * @file pn532_uart.c
 * @brief PN532 driver using UART bus (UART, RS232, etc.)
 */



#include <stdio.h>
#include <inttypes.h>
#include <string.h>
#include "nfc.h"

#include "pn532_uart.h"
#include "nfc-internal.h"
#include "pn53x.h"
#include "pn53x-internal.h"
#include "uart.h"
#include "malloc.h"

#define PN532_UART_DEFAULT_SPEED 115200
#define PN532_UART_DRIVER_NAME "pn532_uart"

// Internal data structs

typedef struct
{
    serial_port port;
    volatile bool abort_flag;
}pn532_uart_data;

nfc_driver pn532_uart_driver;
pn53x_io pn532_uart_io;

// Prototypes
int     pn532_uart_ack(nfc_device *pnd);
int     pn532_uart_wakeup(nfc_device *pnd);

#define DRIVER_DATA(pnd) (( pn532_uart_data*)(pnd->driver_data))



static void pn532_uart_close(nfc_device *pnd)
{
   // pn53x_idle(pnd);

    //elease UART port
    uart_close();

    //pn53x_data_free(pnd);
    nfc_device_free(pnd);
}

#if 1

nfc_device *pn532_uart_open(void)
{
    nfc_device *pnd = NULLP;
//    return NULLP;
    uart_open();

    uart_set_speed();
    // We have a connection
    pnd = nfc_device_new();

    pnd->driver_data = rn_malloc(sizeof(pn532_uart_data));
    if (!pnd->driver_data)
    {
        return NULLP;
    }

    // Alloc and init chip's data
    pn53x_data_new(pnd, &pn532_uart_io);
    // SAMConfiguration command if needed to wakeup the chip and pn53x_SAMConfiguration check if the chip is a PN532
    CHIP_DATA(pnd)->type = PN532;
    // This device starts in LowVBat mode
    CHIP_DATA(pnd)->power_mode = LOWVBAT;

    // empirical tuning
    CHIP_DATA(pnd)->timer_correction = 48;
    pnd->driver = &pn532_uart_driver;

    DRIVER_DATA(pnd)->abort_flag = false;

    // Check communication using "Diagnose" command, with "Communication test" (0x00)
    if (pn53x_check_communication(pnd) < 0)
    {
        pn532_uart_close(pnd);
        return NULLP;
    }

    pn53x_init(pnd);
    return pnd;

}

int pn532_uart_wakeup(nfc_device *pnd)
{
    /* High Speed Unit (HSU) wake up consist to send 0x55 and wait a "long" delay for PN532 being wakeup. */
    const uint8_t pn532_wakeup_preamble[] = { 0x55, 0x55, 0x00, 0x00, 0x00 };
    int res = uart_send(pn532_wakeup_preamble, sizeof(pn532_wakeup_preamble), 0);
    CHIP_DATA(pnd)->power_mode = NORMAL; // PN532 should now be awake
    return res;
}

#define PN532_BUFFER_LEN (PN53x_EXTENDED_FRAME__DATA_MAX_LEN + PN53x_EXTENDED_FRAME__OVERHEAD)
static int pn532_uart_send(nfc_device *pnd, const uint8_t *pbtData, const size_t szData, int timeout)
{
    int res = 0;
    uint8_t  *abtFrame = rn_malloc(PN532_BUFFER_LEN);
    size_t szFrame = 0;
    uint8_t abtRxBuf[6];

    abtFrame[0] = 0x00;
    abtFrame[1] = 0x00;
    abtFrame[2] = 0xff;
    
    switch (CHIP_DATA(pnd)->power_mode)
    {
        case LOWVBAT:
        {
            /** PN532C106 wakeup. */
            if ((res = pn532_uart_wakeup(pnd)) < 0)
            {
                rn_free(abtFrame);
                return res;
            }
            // According to PN532 application note, C106 appendix: to go out Low Vbat mode and enter in normal mode we need to send a SAMConfiguration command
            if ((res = pn532_SAMConfiguration(pnd, PSM_NORMAL, 1000)) < 0)
            {
                rn_free(abtFrame);
                return res;
            }
        }
        break;
        case POWERDOWN:
        {
            if ((res = pn532_uart_wakeup(pnd)) < 0)
            {
                rn_free(abtFrame);
                return res;
            }
        }

        break;
        case NORMAL:
            // Nothing to do :)
            break;
    };

    if ((res = pn53x_build_frame(abtFrame, &szFrame, pbtData, szData)) < 0)
    {
        pnd->last_error = res;
        rn_free(abtFrame);
        return pnd->last_error;
    }

    res = uart_send(abtFrame, szFrame, timeout);
    if (res != 0)
    {
        pnd->last_error = res;
        rn_free(abtFrame);
        return pnd->last_error;
    }

    res = uart_receive(abtRxBuf, 6, 0, timeout);
    if (res != 0)
    {
        pnd->last_error = res;
        rn_free(abtFrame);
        return pnd->last_error;
    }

    if (pn53x_check_ack_frame(pnd, abtRxBuf, sizeof(abtRxBuf)) == 0)
    {
        // The PN53x is running the sent command
    }
    else
    {
        rn_free(abtFrame);
        return pnd->last_error;
    }

    rn_free(abtFrame);
    return NFC_SUCCESS;
}

static int pn532_uart_receive(nfc_device *pnd, uint8_t *pbtData, const size_t szDataLen, int timeout)
{
    uint8_t  abtRxBuf[5];
	size_t szPos;
    size_t len;
    void *abort_p = NULLP;
    const uint8_t pn53x_preamble[3] = { 0x00, 0x00, 0xff };
    uint8_t btDCS = (256 - 0xD5);

    abort_p = (void *) & (DRIVER_DATA(pnd)->abort_flag);

    pnd->last_error = uart_receive( abtRxBuf, 5, abort_p, timeout);

    if (abort_p && (NFC_EOPABORTED == pnd->last_error))
    {
        return pn532_uart_ack(pnd);
    }

    if (pnd->last_error < 0)
    {
        goto error;
    }


    if (0 != (memcmp(abtRxBuf, pn53x_preamble, 3)))
    {
        pnd->last_error = NFC_EIO;
        goto error;
    }

    if ((0x01 == abtRxBuf[3]) && (0xff == abtRxBuf[4]))
    {
        // Error frame
        uart_receive(abtRxBuf, 3, 0, timeout);
        pnd->last_error = NFC_EIO;
        goto error;
    }
    else if ((0xff == abtRxBuf[3]) && (0xff == abtRxBuf[4]))
    {
        // Extended frame
        pnd->last_error = uart_receive(abtRxBuf, 3, 0, timeout);
        if (pnd->last_error != 0)
        {
            goto error;
        }
        // (abtRxBuf[0] << 8) + abtRxBuf[1] (LEN) include TFI + (CC+1)
        len = (abtRxBuf[0] << 8) + abtRxBuf[1] - 2;
        if (((abtRxBuf[0] + abtRxBuf[1] + abtRxBuf[2]) % 256) != 0)
        {
            pnd->last_error = NFC_EIO;
            goto error;
        }
    }
    else
    {
        // Normal frame
        if (256 != (abtRxBuf[3] + abtRxBuf[4]))
        {
            // TODO: Retry
            pnd->last_error = NFC_EIO;
            goto error;
        }

        // abtRxBuf[3] (LEN) include TFI + (CC+1)
        len = abtRxBuf[3] - 2;
    }

    if (len > szDataLen)
    {
        pnd->last_error = NFC_EIO;
        goto error;
    }

    // TFI + PD0 (CC+1)
    pnd->last_error = uart_receive(abtRxBuf, 2, 0, timeout);
    if (pnd->last_error != 0)
    {
        goto error;
    }

    if (abtRxBuf[0] != 0xD5)
    {
        pnd->last_error = NFC_EIO;
        goto error;
    }

    if (abtRxBuf[1] != CHIP_DATA(pnd)->last_command + 1)
    {
        pnd->last_error = NFC_EIO;
        goto error;
    }

    if (len)
    {
        pnd->last_error = uart_receive( pbtData, len, 0, timeout);
        if (pnd->last_error != 0)
        {
            goto error;
        }
    }

    pnd->last_error = uart_receive(abtRxBuf, 2, 0, timeout);
    if (pnd->last_error != 0)
    {
        goto error;
    }


    btDCS -= CHIP_DATA(pnd)->last_command + 1;
    for (szPos = 0; szPos < len; szPos++)
    {
        btDCS -= pbtData[szPos];
    }

    if (btDCS != abtRxBuf[0])
    {
        pnd->last_error = NFC_EIO;
        goto error;
    }

    if (0x00 != abtRxBuf[1])
    {
        pnd->last_error = NFC_EIO;
        goto error;
    }
    // The PN53x command is done and we successfully received the reply
    return len;
error:
    return pnd->last_error;
}

int pn532_uart_ack(nfc_device *pnd)
{
    int res = 0;
    if (POWERDOWN == CHIP_DATA(pnd)->power_mode)
    {
        if ((res = pn532_uart_wakeup(pnd)) < 0)
        {
            return res;
        }
    }
    return (uart_send( pn53x_ack_frame, sizeof(pn53x_ack_frame),  0));
}

static int pn532_uart_abort_command(nfc_device *pnd)
{
    if (pnd)
    {
        DRIVER_DATA(pnd)->abort_flag = true;
    }
    
    return NFC_SUCCESS;
}

#endif
#if 1

void pn532_registerFun(void)
{  
    pn532_uart_io.send       = pn532_uart_send;
    pn532_uart_io.receive    = pn532_uart_receive;


    pn532_uart_driver.name                             = PN532_UART_DRIVER_NAME;
    pn532_uart_driver.scan_type                        = INTRUSIVE;
    pn532_uart_driver.open                             = pn532_uart_open;
    pn532_uart_driver.close                            = pn532_uart_close;

    pn532_uart_driver.initiator_init                   = pn53x_initiator_init;
    pn532_uart_driver.initiator_init_secure_element    = pn532_initiator_init_secure_element;
    pn532_uart_driver.initiator_select_passive_target  = pn53x_initiator_select_passive_target;
    //pn532_uart_driver.initiator_poll_target            = pn53x_initiator_poll_target;
    //pn532_uart_driver.initiator_select_dep_target      = pn53x_initiator_select_dep_target;
    //pn532_uart_driver.initiator_deselect_target        = pn53x_initiator_deselect_target;
    pn532_uart_driver.initiator_transceive_bytes       = pn53x_initiator_transceive_bytes;
    pn532_uart_driver.initiator_transceive_bits        = pn53x_initiator_transceive_bits;
   // pn532_uart_driver.initiator_transceive_bytes_timed = pn53x_initiator_transceive_bytes_timed;
   // pn532_uart_driver.initiator_transceive_bits_timed  = pn53x_initiator_transceive_bits_timed;
   // pn532_uart_driver.initiator_target_is_present      = pn53x_initiator_target_is_present;

    pn532_uart_driver.target_init           = pn53x_target_init;
    pn532_uart_driver.target_send_bytes     = pn53x_target_send_bytes;
    pn532_uart_driver.target_receive_bytes  = pn53x_target_receive_bytes;
    pn532_uart_driver.target_send_bits      = pn53x_target_send_bits;
    pn532_uart_driver.target_receive_bits   = pn53x_target_receive_bits;

    pn532_uart_driver.device_set_property_bool     = pn53x_set_property_bool;
    pn532_uart_driver.device_set_property_int      = pn53x_set_property_int;
    pn532_uart_driver.get_supported_modulation     = pn53x_get_supported_modulation;
    pn532_uart_driver.get_supported_baud_rate      = pn53x_get_supported_baud_rate;
//    pn532_uart_driver.device_get_information_about = pn53x_get_information_about;

    pn532_uart_driver.abort_command  = pn532_uart_abort_command;
    //pn532_uart_driver.idle           = pn53x_idle;
    pn532_uart_driver.powerdown      = pn53x_PowerDown;

    return;
}

#endif

